//1.keyof keyof 与 Object.keys 略有相似，只不过 keyof 取 interface 的键。
// const data = {
//     a: 3,
//     hello: "world",
//   };
//   // 1.无法确认返回类型：这将损失 ts 最大的类型校验功能
//   // 2.无法对 key 做约束：可能会犯拼写错误的问题
//   // function get(o: object, name: string) {
//   //   return o[name];
//   // }
//   function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
//     return o[name];
//   }
//   const data1 = get(data, "hello");
//   console.log(data1);

//----------------------------------------------------------------
//2.Condition Type
// type isTrue<T> = T extends true ? true : false
// // 相当于 type t = false
// type t = isTrue<number>

// // 相当于 type t = false
// type t1 = isTrue<false>

//----------------------------------------------------------------
// 3.typeof 代表取某个值的 type，可以从以下示例来展示他们的用法
// const a: number = 3

// // 相当于: const b: number = 4
// const b: typeof a = 4
// //在一个典型的服务端项目中，我们经常需要把一些工具塞到 context 中，如config，logger，db models, utils 等，此时就使用到 typeof。
// import logger from './logger'
// import utils from './utils'

// interface Context extends KoaContect {
//   logger: typeof logger,
//   utils: typeof utils
// }

// app.use((ctx: Context) => {
//   ctx.logger.info('hello, world')

//   // 会报错，因为 logger.ts 中没有暴露此方法，可以最大限度的避免拼写错误
//   ctx.loger.info('hello, world')
// });

//----------------------------------------------------------------
// 4.is
// if ((err as AxiosError).isAxiosError) {
//     code = `Axios-${(err as AxiosError).code}`
//   }
// function isAxiosError(error: any): error is AxiosError {
//   return error.isAxiosError;
// }

// if (isAxiosError(err)) {
//   code = `Axios-${err.code}`;
// }
//----------------------------------------------------------------
//----------------------------------------------------------------
//----------------------------------------------------------------

function tuplity<T extends unknown[]>(...elements: T): T {
  return elements;
}

function test() {
  // let data = ['adasd', 122] as const;
  let name = '小月';
  let age = 18;
  let data = <const>[name, age];
  // return data;
  return tuplity(name, age);
}
const items = test();

let [f] = items;

interface a {
  <T>(data: T): string;
}

const data = {
  a: 3,
  hello: 'hello',
};

function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
  return o[name];
}

const result = get(data, 'hello');

interface A {
  <T>(age: T): number;
}

type Test = <T>(age: T) => number;
const getData: A = <T>(age: T): number => {
  // console.log(age);
  // return age as unknown as number;
  return <number>(age as unknown);
};

class Person {
  name = 'xiaozhu';
  age = 20;
}

class Animal {
  name = 'xiaozhu';
  color = 'red';
}

// 类型守卫
function getSomething(arg: Person | Animal) {
  // if (arg instanceof Person) {
  //   console.log(arg.color);
  //   console.log(arg.age);
  // }

  // if (arg instanceof Animal) {
  //   console.log(arg.color);
  //   console.log(arg.age);
  // }

  if ('age' in arg) {
    console.log(arg.color);
    console.log(arg.age);
  }

  if ('color' in arg) {
    console.log(arg.color);
    console.log(arg.age);
  }
}

function mixin<T extends object, U extends object>(first: T, second: U): T & U {
  const result = <T & U>{};
  for (const id in first) {
    (<T>result)[id] = first[id];
  }
  for (const id in second) {
    if (!result.hasOwnProperty(id)) {
      (<U>result)[id] = second[id];
    }
  }
  return result;
}

const x = mixin({ a: 'hello' }, { b: 42 });
// 现在x拥有了a属性和b属性
console.log(x.a);
console.log(x.b);

declare function fun<T extends boolean>(x: T): T extends true ? string : number;
const x3 = fun(Math.random() < 0.5);
const y3 = fun(false);
const z = fun(true);

// 又一个interface Part，现在写一个工具函数将interface中函数的名称取出来
interface Part {
  id: number;
  name: string;
  subparts: string[];
  updatePart(newName: string): void;
  updatePart1(newName: string): void;
}
type FunctionPropertyNames<T> = {
  [K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type R3 = FunctionPropertyNames<Part>;
// 1假设我们吧Part带入泛型T，[K in keyof T]相当月遍历整个interface
// 2这是K相当于interface的key，T[K]相当于interface的value
// 3接下来，用条件类型验证value的类型，如果是Function那么将value作为新的interface的key保留下来，否则为never
// 4到这里我们得到了遍历修改后的interface即：
// type R7 = {
//   id: never;
//   name: never;
//   subparts: never;
//   updatePart: 'updatePart';
// }[keyof Part];
// type T = keyof Part;
// 但是我们的要求是取出interface Part中的key，这个时候再次用[keyof T]作为key以此取出新的interface中的value
// 但是由于id name等value是never就不会返回任何类型了，所以只返回了函数类型的updatePart
